﻿application.allowDebug = true;

// 载入服务器端函数
load("serverFunction.asc")


// 启动服务器并初始化
application.onAppStart = function() {
	application.meetingList = SharedObject.get("meetingList", true);	// 获取会议列表
	application.memberList = SharedObject.get("memberList", false);		// 获取成员列表(在线成员)
	application.meeting = SharedObject.get("meeting", false);			// 会议中人员
	application.im = SharedObject.get("im", false);						// IM在线人员
	application.chat = SharedObject.get("chat", false);					// 正在视频聊天的人员
	application.notify = SharedObject.get("notify", true);				// 獲取會議通知
	
	// 将标识都初始化为1，在这里会议编号为0是因为在下面有自加操作
	application.memberServerId = 1;	// 初始化成员服务器端标识
	application.meetingId = 0;		// 初始化会议编号
	
	// 遍历会议列表，找出最后创建的会议的会议编号
	var o;
	var tmp = "";
	var names = application.meetingList.getPropertyNames();
	
	trace(logTime() + "    系统启动于：" + new Date());
	for (x in names) {
		o = application.meetingList.getProperty(names[x]);
		if (o != null) {
			application.meetingId = o.id;
		}
	}
	
	showAllMeeting();
	showAllNotify();
	// 会议编号自增1，用于下一个会议的创建
	application.meetingId++;
	
	trace(logTime() + "    初始化会议编号：" + application.meetingId);
	trace(logTime() + "    初始化在线成员编号：" + application.memberServerId);
};


/**
 * @des 接受客户端连接请求并验证
 * @param client 客户端连接对象
 * @param memberId 成员ＩＤ
 * @param memberName 成员名称
 * @param loginType 连接类型，1 聊天登录　2 会议登录 3 正在与别人私聊．
 *        当loginType 为3时，memberId 为发送人ID与发送人名称，用冒号分隔，memberName 为接收人ID与接收人名称，用冒号分隔
 */
application.onConnect = function(client, memberId, memberName, loginType) {
	
	// 校验当前连接的合法性
	if (chkMember(client, memberId)) {
		// 接受client的连接
		application.acceptConnection(client);
	}
	
	
	/**
	 * @des 保存当前客户端数据
	 */
	if (memberId != "") {
		saveMemberData(client, memberId, memberName, loginType);
	}
	
	
	/**
	 * @des 打印客户端错误
	 * @param str 错误描述
	 */
	client.upLog = function(str) {
		trace(logTime() + "客户端信息:" + str);
	}
	
	
	/**
	 * @des 创建会议，时间格式为十二位数字串，如：200804031532
	 * @param meetingMode 会议模式　only（大会模式）  all（讨论会模式）
	 * @param meetingName 会议名称
	 * @param meetingTheme 会议主题
	 * @param startTime 开会时间
	 * @param selectedItemsString 成员列表，包括成员ID与成员名称，用冒号（:）分隔，多成员之间用逗号（,）分隔
	 *        格式：成员名称1:成员ID1,成员名称2:成员ID2,...,成员名称n:成员IDn,
	 * @param createUser 创建人，包括成员ID与成员名称，用冒号（:）分隔
	 * @param createTime 创建时间
	 */
	client.newMeeting = function(meetingMode,
								 meetingName,
								 meetingTheme,
								 selectedItemsString,
								 createUser,
								 createTime,
								 isSend,
								 notifyContent) {
		
		// 将创建人加入到与会人员列表中
		if (selectedItemsString.indexOf(createUser + ",") == -1) {
			selectedItemsString = createUser + "," + selectedItemsString;
		}
		
		// 将省厅中心_administrator加入到与会人员列表中
		if (selectedItemsString.indexOf('省中心_administrator') == -1) {
			selectedItemsString += '省中心_administrator:省中心_超级管理员';
		}
		
		var meetingObject = new Object();
		meetingObject.id = application.meetingId++;
		meetingObject.meetingMode = meetingMode;
		meetingObject.meetingName = meetingName;
		meetingObject.meetingTheme = meetingTheme;
		meetingObject.memberList = selectedItemsString;
		meetingObject.createUser = createUser;
		meetingObject.createTime = createTime;
		meetingObject.allowPrivateChat = false;
		meetingObject.showAll = false;
		meetingObject.currentProlocutor = "";		// 会议室的当前发言人
		
		// 添加会议
		application.meetingList.setProperty(meetingObject.id, meetingObject);
		
		// 广播创建会议成功的消息
		client.call("showMsg", null, "会议创建成功！");
		
		trace("\n" + logTime() + "\n===========================================\n" +
			  "=    创建会议成功：\n" +
			  "=    会议编号：" + (parseInt(application.meetingId) - 1) + "\n" +
			  "=    会议模式：" + meetingMode + "\n" +
			  "=    会议名称：" + meetingName + "\n" +
			  "=    会议主题：" + meetingTheme + "\n" +
			  "=    与会人员：" + selectedItemsString + "\n" +
			  "=    允许私聊：false\n" +
			  "=    显示所有聊天内容：false\n" +
			  "=    创建人：" + createUser + "\n" +
			  "=    创建时间：" + createTime + "\n" +
			  "===========================================\n");
		
		/**
		 * @des 如果需要派發通知，那么將通知人員與內容存入notify共享變量中．
		 */
		if (isSend == "yes") {
			var object = new Object();
			object.id = meetingObject.id;
			object.meetingName = meetingName;
			object.meetingTheme = meetingTheme;
			object.createUser = createUser;
			object.createTime = createTime;
			object.memberList = selectedItemsString;
			object.notifyContent = notifyContent;
			
			// 添加通知
			application.notify.setProperty(object.id, object);
		}
	};
	
	
	/**
	 * @des 更新当前客户端所参与的会议
	 * @param meetingId 加入的会议Id,如果参数为空,则认为是离开会场
	 */
	client.updateCurrentMeeting = function(meetingId) {
		var str;
		var chatMode;
		
		if (meetingId != "") {
			if (application.meetingList.getProperty(meetingId).allowPrivateChat) {
				chatMode = "开启";
			} else {
				chatMode = "关闭";
			}
		}
		
		client.currentMeetingId = meetingId;
		
		if (meetingId == "") {
			str = "离开";
			trace(logTime() + "    会议室:" + client.currentMeeingId + '中的:' + client.memberName + '离开会场');
		} else {
			str = "进入";
		}
		
		// 将当前成员保存到meeting共享变量中,用于聊天工具中的人员共同
		saveMeetingMember(meetingId, client.memberId, client.memberName);
		
		broadcastCmd(meetingId,
					 "updateMsg",
					 ":",
					 "<font color=\"#FF0000\"><b>系统消息</b>： <i><u>" + client.memberName + "</u></i> " + str + "会议室...</font><br>",
					 client.loginType);
		broadcastCmd(meetingId,
					 "updateMsg",
					 ":",
					 "<font color=\"#FF0000\"><b>系统消息</b>： 私聊模式 <b>" + chatMode + "</b></font><br>",
					  client.loginType);
		if (str == '离开') {
			trace(logTime() + "    发送clearVideo命令。。。");
			broadcastCmd(meetingId, 'clearVideo', client.memberId, client.loginType);
		}
	};
	
	
	/**
	 * @des 删除会议
	 * @param 要删除的会议Id
	 */
	client.deleteMeeting = function(id) {
		application.meetingList.setProperty(id, null);
		client.currentMeetingId = "";
		
		trace(logTime() + "    会议编号为 " + id + " 的会议被删除...");
		
		// 广播创建会议成功的消息
		client.call("showMsg", null, "会议删除成功！");

		// 广播清除命令,强制结束进入当前会议的人员
		broadcastCmd(id, "clearMeetingPlace", "", "",  client.loginType);
	};
	
	
	/**
	 * @des 设置聊天模式，即是否允许在会议模式下私聊
	 * @param flag 布尔值，true 开启私聊,false关闭私聊
	 */
	client.setChatMode = function(flag) {
		var str;
		
		if (flag) {
			str = "开启";
		} else {
			str = "关闭";
		}
		
		var o = application.meetingList.getProperty(client.currentMeetingId);
		
		// 判断是否改变了状态，如果改变则重新设置
		if (o.allowPrivateChat != flag) {
			// 设置新的状态
			o.allowPrivateChat = flag;
			// 保存状态
			application.meetingList.setProperty(client.currentMeetingId, o);
			// 广播更新状态
			broadcastCmd(client.currentMeetingId, "setPrivateChat", client.memberId, flag,  client.loginType);
			// 将状态广播到公告板
			broadcastCmd(client.currentMeetingId,
						 "updateMsg",
						 ":",
						 "<font color=\"#FF0000\"><b>系统消息</b>： 私聊功能 <b>" + str + "</b></font><br>",
						  client.loginType);
			trace(logTime() + "    会议室:" + client.currentMeeingId + '状态修改:私聊功能:' + str);
		}
	};
	
	
	/**
	 * @des 设置主持人是否显示所有聊天内容
	 * @param flag 布尔值，是否显示所有聊天内容
	 */
	client.setShowAll = function(flag) {
		var o = application.meetingList.getProperty(client.currentMeetingId);
		
		o.showAll = flag;
		
		application.meetingList.setProperty(client.currentMeetingId, o);
		
		broadcastCmd(client.currentMeetingId, "setShowAll", client.memberId, flag,  client.loginType);
		trace(logTime() + "    会议室:" + client.currentMeetingId + '状态修改:' + (flag ? '显示' : '不显示') + '所有聊天内容.');
	};
	
	
	/**
	 * @des 申请发言
	 * @param meetingId 会场ID
	 * @param memberName 成员名称，即申请人
	 */
	client.applySpeak = function(meetingId, memberName) {
		trace(logTime() + "    " + memberName + "(会议室：" + meetingId + ")申请发言...");
		broadcastCmd(meetingId, "applySpeak", memberName,  client.loginType);
	};
	
	
	/**
	 * @des 切换发言人
	 * @param proposer 要切换至的发言人ID
	 * @param flag 布尔值，是否同意其发言．
	 */
	client.switchProlocutor = function(proposerId, flag) {
		var str;
		if (flag) {
			str = "同意";
		} else {
			str = "不同意";
		}
		
		trace(logTime() + "    主持人" + str + " " + proposerId + "发言...");
		
		if (flag) {
			var o = application.meetingList.getProperty(client.currentMeetingId);
			
			if (typeof(o) == "object") {
				broadcastCmd(client.currentMeetingId, "modifyCamera", o.currentProlocutor, true,  client.loginType);
			}
			// 重置发言人
			o.currentProlocutor = proposerId;
			application.meetingList.setProperty(client.currentMeetingId, o);
			
			broadcastCmd(client.currentMeetingId, "switchProlocutor", proposerId, "", client.loginType);
		} else {
			for (var i = 0; i < application.clients.length; i++) {
				if (application.clients[i].memberId == proposerId) {
					application.clients[i].call("showMsg", null, "主持人拒绝您的发言申请！");
					break;
				}
			}
		}
	};
	
	
	/**
	 * @des 停止发言
	 * @param proposerId 要停止发言的成员ID
	 */
	client.halt = function(proposerId) {
		broadcastCmd(client.currentMeetingId, "stopSpeak", proposerId, "", client.loginType);
		trace(logTime() + '    停止了' + proposerId + '的发言...');
	};
	
	
	/**
	 * @des 更新会议室成员列表
	 * @param newMemberList 新的成员列表
	 */
	client.updateMeeting = function(newMemberList) {
		var o, names;
		
		names = application.meetingList.getPropertyNames();
		
		for (x in names) {
			o = application.meetingList.getProperty(names[x]);
			
			if (o != null) {
				if (o.id == client.currentMeetingId) {
					o.memberList = newMemberList;
					application.meetingList.setProperty(o.id, o);
				}
			}
		}
	};
	
	
	/**
	 * @des 更新視頻列表
	 * @param memberList 新的人員列表
	 */
	 client.updateVideoList = function(memberList) {
		 broadcastCmd(client.currentMeetingId, "updateVideoList", memberList, "", client.loginType);
	 };
	 
	 
	 /**
	  * 广播当前用户退出的消息
	  */
	 /*
	 client.logout = function() {
		 trace(logTime() +"    " + memberName + " 离开了会场，清理此用户的视频信息...");
		 broadcastCmd(client.currentMeetingId, 'clearVideo', client.memberId, client.loginType);
	 };
	*/
	
	/**
	 * @des 更新客户端摄像头的采样率
	 * @param meetingId 要更新的客户端所在的会议ID
	 * @param memberId 要更新的客户端ID
	 * @param flag 采样模式　true,发言席模式(320*300) false,小窗口模式(160*120)
	 */
	client.modifyCamera = function(meetingId, memberId, flag) {
		broadcastCmd(meetingId, "modifyCamera", memberId, flag,  client.loginType);
	};
	
	
	/**
	 * @des 发送消息
	 * @param memberId 发送人
	 * @param to 接收人，为空时面向所有人
	 * @param meetingId 会议Id
	 * @param msg 消息内容
	 */
	client.sendMsg = function(memberId, to, meetingId, msg) {
		/**
		 * @des 对于broadcastCmd中第三个参数，在这里进行特殊处理
		 *      本来应该只是发送消息者的Id,在这里后面再添加接收人名称
		 *      由于从客户端传来的是接收人的名称，因此要特殊处理
		 */
		broadcastCmd(meetingId, "updateMsg", memberId + ":" + to, msg,  client.loginType);
	};
	
	
	/**
	 * @des 打開對方的聊天窗口
	 * @param receiverId 對方id
	 * @param receiverName 對方名稱
	 */
	client.applyChat = function(receiverId, receiverName, timer) {
		for (var i = 0; i < application.clients.length; i++) {
			if (application.clients[i].loginType == 1 && application.clients[i].memberId == receiverId) {
				application.clients[i].call("openWin",
											null,
											client.memberId,
											client.memberName,
											timer);
			}
		}
	};
	
	
	/**
	 * @des 取消客户端的通知提示
	 * @param meetingIdList 要取消的通知提示所属的会议室ID列表，用逗号分隔
	 * @param memberName 要取消通知提示的用户名称　
	 * @param memberId 要取消通知提示的用户ID
	 */
	client.cancelNotify = function(meetingIdList, memberName, memberId) {
		var meetingIdArray = meetingIdList.split(",");
		
		for (var i = 0; i < meetingIdArray.length; i++) {
			if (meetingIdArray[i] != "") {
				var o = application.notify.getProperty(meetingIdArray[i]);
				o.memberList = o.memberList.replace(memberName + ":" + memberId + ",", "");
				trace('.......' + memberName + ":" + memberId + ',');
				/**
				 * @des 如果列表为空的话，则删除此通知
				 */
				if (o.memberList.length < 1) {
					application.notify.setProperty(o.id, null);
				} else {
					application.notify.setProperty(o.id, o);
				}
			}
		}
		trace(logTime() +"    " + memberName + " 取消了會議提示...");
		showAllNotify();
	};
};


/**
 * @des 当客户端与服务器端断开连接时
 */
application.onDisconnect = function(client) {
	switch (client.loginType) {
		case 1:
			trace(logTime() + "    " + client.memberName + " 退出聊天工具...");
			application.im.setProperty(client.memberId, null);
			break;
		case 2:
			/**
			 * @des广播消息,告诉客户端此用户已经下线.
			 *  对于上下线状态的广播,不指定会场,对于所有会场都是可接受的
			 */
			broadcastCmd(client.currentMeetingId,
						 "updateMsg",
						 ":",
						 "<font color=\"#FF0000\"><b>系统消息</b>： <i><u>" + client.memberName + "</u></i> 已经退出当前会议...</font><br>",
						  client.loginType);
		
			// 如果当前退出的成员正在发言，则清空
			var o = application.meetingList.getProperty(client.currentMeetingId);
			if (o != null) {
				if (o.currentProlocutor == client.memberId) {
					o.currentProlocutor = "";
					application.meetingList.setProperty(client.currentMeetingId, o);
				}
			}
		
			// 广播停止发言的消息
			broadcastCmd(client.currentMeetingId, "stopSpeak", client.memberId,  client.loginType);
			
			// 广播清理客户端此断线用户的图像
			broadcastCmd(client.currentMeetingId, 'clearVideo', client.memberId, client.loginType);
			
			// 清理成员列表
			application.meeting.setProperty(client.memberId, null);
			
			// 清理当前用户的stream流
			var clearResult = application.clearStreams(client.memberId);
			
			trace(logTime() + "    " + client.memberName + "(" + (client.loginType == 1 ? "聊天工具" : "会议室") + ") 与服务器断开连接...");
			if (clearResult) {
				trace(logTime() + "    " + client.memberName + "(ID:" + client.memberId + ") 的视频流清理成功．．．");
			} else {
				trace(logTime() + "    " + client.memberName + "(ID:" + client.memberId + ") 的视频流清理失败．．．");
			}
			break;
		case 3:
			trace(logTime() + "    " + client.memberId.split(":")[1] + " 退出聊天窗口...");
			
			/**
			 * @des 给自己发送一条更新用户列表的信息
			 *      参数为对方的ID，由于存放时memberName中存放的是复合性的数据，因此要先拆分．
			 */
			for (var i = 0; i < application.clients.length; i++) {
				if (application.clients[i].loginType == 1 && application.clients[i].memberId == client.memberId.split(":")[0]) {
					application.clients[i].call("updateState", null, client.memberName.split(":")[0]);
				}
			}
			application.chat.setProperty(client.memberId.split(":")[0], null);
			break;
	}
	if (isOffline(client.memberId)) { application.memberList.setProperty(client.memberId, null); }
};